package com.yanmaohu.nikita;

import com.yanmaohu.nikita.data.expression.NameExpression;
import com.yanmaohu.nikita.scanner.SourceCode;
import com.yanmaohu.nikita.scanner.Token;

/**
 * @author Bright Lee
 */
public final class NikitaException extends RuntimeException {

    private SourceCode sourceCode;
    private int beginIndex;
    private int endIndex;

    public NikitaException(SourceCode sourceCode, final int beginIndex, int endIndex, String message) {
        super(message);
        this.sourceCode = sourceCode;
        /*int theBeginIndex = beginIndex;
        for (int i = beginIndex - 1; i >= 0; i--) {
            int c = code.charAt(i);
            if (!Character.isWhitespace(c)) {
                theBeginIndex = i + 1;
                break;
            }
        }
        boolean b = false;
        for (int i = theBeginIndex; i < beginIndex; i++) {
            int c = code.charAt(i);
            if (c == '\r' || c == '\n') {
                b = true;
                break;
            }
        }
        if (b == false) {
            theBeginIndex = beginIndex;
        }
        this.beginIndex = theBeginIndex;*/
        this.beginIndex = beginIndex;
        this.endIndex = endIndex;
    }

    public NikitaException(SourceCode sourceCode, Token token, String message) {
        this(sourceCode, token.getBeginIndex(), token.getEndIndex(), message);
    }

    public NikitaException(SourceCode sourceCode, NameExpression nameExp, String message) {
        this(sourceCode, nameExp.getBeginIndex(), nameExp.getEndIndex(), message);
    }

    public SourceCode getCode() {
        return sourceCode;
    }

    public int beginIndex() {
        return beginIndex;
    }

    public int endIndex() {
        return endIndex;
    }

    public String getMessage() {
        StringBuilder buf = new StringBuilder();
        buf.append("===> ").append(sourceCode.getFile().getAbsolutePath()).append('\n');
        buf.append(super.getMessage()).append('\n');
        int row = 0;
        int rowBeginIndex;
        int rowEndIndex;
        for (int i = 0; i < sourceCode.length();) {
            rowBeginIndex = i;
            rowEndIndex = -1;
            for (int j = i; j < sourceCode.length(); j++) {
                int c = sourceCode.charAt(j);
                if (c == '\r') {
                    row++;
                    rowEndIndex = j;
                    i = j + 1;
                    if (j + 1 < sourceCode.length()) {
                        int c2 = sourceCode.charAt(j + 1);
                        if (c2 == '\n') {
                            i = j + 1 + 1;
                        }
                    }
                    break;
                } else if (c == '\n') {
                    row++;
                    rowEndIndex = j;
                    i = j + 1;
                    break;
                } else {
                    if (j == sourceCode.length() - 1) {
                        row++;
                        rowEndIndex = j + 1;
                        i = j + 1;
                        break;
                    } else {
                        rowEndIndex = j;
                        i = j + 1;
                        continue;
                    }
                }
            }
            if (row < 1) {
                row = 1;
            }
            if (rowEndIndex < 0) {
                rowEndIndex = sourceCode.length();
            }
            if (beginIndex >= rowBeginIndex && beginIndex <= rowEndIndex) {
                String srow = String.valueOf(row);
                buf.append(srow).append(':').append(' ');
                for (int k = rowBeginIndex; k < rowEndIndex; k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                buf.append('\n');
                for (int k = 0; k < srow.length() - 1; k++) {
                    buf.append(' ');
                }
                buf.append('*').append(':').append(' ');
                for (int k = rowBeginIndex; k < beginIndex; k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                if (endIndex >= rowBeginIndex && endIndex <= rowEndIndex) {
                    buf.append('\n');
                    for (int k = 0; k < srow.length() - 1; k++) {
                        buf.append(' ');
                    }
                    buf.append('*').append(':').append(' ');
                    for (int k = rowBeginIndex; k < endIndex; k++) {
                        buf.append((char) sourceCode.charAt(k));
                    }
                    break;
                }
                break;
            }
            if (endIndex >= rowBeginIndex && endIndex <= rowEndIndex) {
                String srow = String.valueOf(row);
                buf.append(srow).append(':').append(' ');
                for (int k = rowBeginIndex; k < rowEndIndex; k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                buf.append('\n');
                for (int k = 0; k < srow.length() - 1; k++) {
                    buf.append(' ');
                }
                buf.append('*').append(':').append(' ');
                for (int k = rowBeginIndex; k < endIndex; k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                break;
            }
            if (i >= sourceCode.length()) {
                if (sourceCode.length() == 0) {
                    row = 1;
                } else {
                    int c = sourceCode.charAt(sourceCode.length() - 1);
                    if (c != '\n' && c != '\r') {
                        row++;
                    }
                }
                String srow = String.valueOf(row);
                buf.append(srow).append(':').append(' ');
                for (int k = rowBeginIndex; k < sourceCode.length(); k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                buf.append('\n');
                for (int k = 0; k < srow.length() - 1; k++) {
                    buf.append(' ');
                }
                buf.append('*').append(':').append(' ');
                for (int k = rowBeginIndex; k < beginIndex; k++) {
                    buf.append((char) sourceCode.charAt(k));
                }
                break;
            }
        }
        return buf.toString();
    }

}
